package com.alan.shiro.api.config;

import com.alan.shiro.api.filter.StatelessAuthcFilter;
import com.alan.shiro.api.realm.StatelessRealm;
import com.alan.shiro.api.service.UserService;
import com.alan.shiro.api.subject.StatelessDefaultSubjectFactory;
import com.alan.shiro.api.token.helper.UserTokenOperHelper;
import com.alan.shiro.api.token.helper.UserTokenOperHelperImpl;
import com.alan.shiro.api.token.manager.TokenManager;
import com.alan.shiro.api.token.manager.impl.DefaultTokenManagerImpl;
import com.alan.shiro.api.token.manager.impl.RaFilterJwtTokenManagerImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.AnonymousFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Auther: Alan liu
 * @Date: 2018/12/11 18:14
 * @Description: ShiroConfiguration
 */
@Configuration
@Slf4j
public class ShiroConfiguration {

    @Bean
    public TokenManager tokenManager(ShiroProperties shiroProperties){
        log.info("ShiroConfig.getTokenManager()");
        //默认的token管理实现类 32位uuid
        DefaultTokenManagerImpl tokenManager = new DefaultTokenManagerImpl();
        //token失效时间
        tokenManager.setExpirateTime(shiroProperties.getExpirateTime());
        //用户token管理类
        UserTokenOperHelper userTokenOperHelper = new UserTokenOperHelperImpl();
        tokenManager.setUserTokenOperHelper(userTokenOperHelper);

        // 安全的jwttoken方式 不用担心token被拦截
        RaFilterJwtTokenManagerImpl jwtTokenManager = new RaFilterJwtTokenManagerImpl();
        jwtTokenManager.setExpirateTime(shiroProperties.getExpirateTime());
        jwtTokenManager.setKey(shiroProperties.getKey());
        return jwtTokenManager;
    }

    /**
     * 无状态域
     * @param tokenManager
     * @param userService 登陆账号服务需要实现PrincipalService接口
     * @return
     */
    @Bean
    public StatelessRealm statelessRealm(TokenManager tokenManager, @Qualifier("userService") UserService userService){
        log.info("ShiroConfig.getStatelessRealm()");
        StatelessRealm realm = new StatelessRealm();
        realm.setTokenManager(tokenManager);
        realm.setUserService(userService);
        return realm;
    }

    /**
     * 会话管理类 禁用session
     * @return
     */
    @Bean
    public DefaultSessionManager defaultSessionManager(){
        log.info("ShiroConfig.getDefaultSessionManager()");
        DefaultSessionManager manager = new DefaultSessionManager();
        manager.setSessionValidationSchedulerEnabled(false);
        return manager;
    }


    /**
     * 安全管理类
     * @return
     */
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(StatelessRealm statelessRealm) {
        log.info("ShiroConfig.getDefaultWebSecurityManager()");
        DefaultWebSecurityManager manager  = new DefaultWebSecurityManager();
        //禁用sessionStorage
        DefaultSubjectDAO de = (DefaultSubjectDAO)manager.getSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator =(DefaultSessionStorageEvaluator)de.getSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);

        manager.setRealm(statelessRealm);

        //无状态主题工程，禁止创建session
        StatelessDefaultSubjectFactory statelessDefaultSubjectFactory = new StatelessDefaultSubjectFactory();
        manager.setSubjectFactory(statelessDefaultSubjectFactory);

        manager.setSessionManager(defaultSessionManager());
        //设置了SecurityManager采用使用SecurityUtils的静态方法 获取用户等
        SecurityUtils.setSecurityManager(manager);
        return manager;
    }
    /**
     * 身份验证过滤器
     * @param manager
     * @param tokenManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager manager,TokenManager tokenManager){
        log.info("ShiroConfig.getShiroFilterFactoryBean()");
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(manager);
        Map<String, Filter> filters = new HashMap<String,Filter>();
        //无需增加 shiro默认会添加该filter
        //filters.put("anon", anonymousFilter());

        //无状态授权过滤器
        //特别注意！自定义的StatelessAuthcFilter
        //不能声明为bean 否则shiro无法管理该filter生命周期，该过滤器会执行其他过滤器拦截过的路径
        //这种情况通过普通spring项目集成shiro不会出现，boot集成会出现，搞了好久才明白，巨坑
        StatelessAuthcFilter statelessAuthcFilter = statelessAuthcFilter(tokenManager);
        filters.put("statelessAuthc", statelessAuthcFilter);
        bean.setFilters(filters);
        //注意是LinkedHashMap 保证有序
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();

        //1， 相同url规则，后面定义的会覆盖前面定义的(执行的时候只执行最后一个)。
        //2， 两个url规则都可以匹配同一个url，只执行第一个
        //3， 使用无状态token方式时，不能在 filterChainDefinitionMap 过用角色权限控制，可以通过java注解，原因如上 第2点
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/favicon.ico", "anon");
        filterChainDefinitionMap.put("/**", "statelessAuthc");

        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        //字符串方式创建过滤链 \n换行
//        String s = "/resource/**=anon\n/html/**=anon\n/login/**=anon\n/login=anon\n/**=statelessAuthc";
//        bean.setFilterChainDefinitions(s);

        return bean;
    }

    /**
     *
     * @Function: ShiroConfig::anonymousFilter
     * @Description: 该过滤器无需增加 shiro默认会添加该filter
     * @return
     */
    public AnonymousFilter anonymousFilter(){
        log.info("ShiroConfig.anonymousFilter()");
        return new AnonymousFilter();
    }

    /**
     *
     * @Function: ShiroConfig::statelessAuthcFilter
     * @Description:  无状态授权过滤器 注意不能声明为bean 否则shiro无法管理该filter生命周期，<br>
     * 				  该过滤器会执行其他过滤器拦截过的路径
     * @param tokenManager
     * @return
     */
    public StatelessAuthcFilter statelessAuthcFilter(TokenManager tokenManager){
        log.info("ShiroConfig.statelessAuthcFilter()");
        StatelessAuthcFilter statelessAuthcFilter = new StatelessAuthcFilter();
        statelessAuthcFilter.setTokenManager(tokenManager);
        return statelessAuthcFilter;
    }


    /**
     * 加入注解的使用，不加入这个注解不生效
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

}
